//This is a trivial C program that takes as input a string of the
//form: "[a-zA-Z0-9]-[a-zA-Z0-9]".  It then expands.  eg:

// "a-e2-4" will expand into "abcde234"

// We will hereafter refer to any three length strength of the form:
// "[a-zA-Z0-9]-[a-zA-Z0-9]" a sequence.

//This exercise is taken from "The C Programming Language" p. 63
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define ALLOCATE_SEQUENCE() (struct sequence_t *)       \
  malloc (sizeof (struct sequence_t))

/* This would expand the sequence "a-e" into "abcde".  This macro
   works on one sequence at a time.  So it will not process two
   sequences like "a-e4-6" at once.  It returns a pointer to the
   expanded string. */
#define EXPAND_SEQUENCE_TO_STRING(loop_str)                             \
  for (char c = sequence->beginning_char;                               \
       c <= sequence->end_char; c++, i++)                               \
    *(loop_str + i) = c;                                                \
  *(loop_str + i) = '\0';                                               

#define IS_CHAR_A_DIGIT(c)  (c >= '0' && c <= '9') ? 1 : 0 

#define EXIT_IF_INVALID_SEQUENCE(a, b)                                  \
  if ((isalpha (a)  && !(isalpha (b)))                                  \
      || (isdigit (a)  && !(isdigit (b)))                               \
      || (isupper (a) && (islower (b)))                                 \
      || (islower (a) && (isupper (b))))                                \
    {                                                                   \
  fprintf (stderr, "Input String \"%c-%c\" is not valid\n",             \
           a, b);                                                       \
  fprintf (stderr,                                                      \
           "Sequences are of the form ([a-bA-B0-9]-[a-bA-B0-9])+.\n");  \
  exit (EXIT_FAILURE);                                                  \
  } 

/* This is the number of sequences the inputted string has. */
int sequences = 0;

/* sequence_t will store the information from an encoded string the
** encoded strings looks like "[a-zA-Z0-9]-[a-zA-Z0-9]".  So an
** encoded string of "a-z", represents the whole alphabet. */

//A struct of
//sequence_t = {beginning_char = 'a', end_char = 'z', next
// = sequence_t { beginning_char = '0', end_char = '9', next = 0 }}
// represents a string that contains the alphabet followed by the
// numbers 1-9.  The original string would have been "a-z0-9".
struct sequence_t
{
  char beginning_char, end_char;
  unsigned int sequence_length;
  struct sequence_t *next;
};

void *
xmalloc (size_t size)
{
  void *value = malloc (size);
  if (value == 0)
    {
      fprintf (stderr, "virtual memory exhausted\n");
      exit (EXIT_FAILURE);
    }
  return value;
}

/* This function parses the string of sequences into numerous struct
   sequence_t.  It returns the pointer to the first sequence. */
struct sequence_t * parse_string (char * input_string)
{
  int str_len = strlen (input_string);
  if (str_len % 3)
    {
      fprintf
        (stderr,
         "Sequences are of the form ([a-bA-B0-9]-[a-bA-B0-9])+.\n");
      exit (EXIT_FAILURE);
    }
  // This is the number of sequences that we have. (reminder "a-c0-9"
  //is 2 sequences).
  extern int sequences;
  sequences = str_len / 3;
  struct sequence_t * sequence = ALLOCATE_SEQUENCE ();
  struct sequence_t * loop_sequence = sequence; 
  for (int i, j = 0; j < sequences; i++, j++)
    {
      loop_sequence->beginning_char = *(input_string + i);
      i += 2; //skip the '-'
      loop_sequence->end_char = *(input_string + i);
      EXIT_IF_INVALID_SEQUENCE
        (sequence->beginning_char, sequence->end_char);
      loop_sequence->next = ALLOCATE_SEQUENCE (); 
      loop_sequence = loop_sequence->next;
    }
  return sequence;
}

/* This function returns expanded sequences from the inputted string
   of sequences. */
char * expand (char * input_string)
{
  struct sequence_t * sequence = parse_string (input_string);
  extern int sequences;
  //this is using way too much memory.  For the sequence "a-b", 32
  //chars are allocated for it.
  char * return_string = (char *) xmalloc (sizeof (char) * 32 * sequences);
  int return_string_len = 0;
  int i;
  //this should work in place of the next line...memset (loop_str, 0, 32);
  char * loop_str = (char *) xmalloc (sizeof (char) * 32);
  while (sequence)
    {
      i = 0;
      EXPAND_SEQUENCE_TO_STRING (loop_str);
      strcpy ((return_string + return_string_len), loop_str);
      return_string_len += i;
      sequence = sequence->next;
    }
  free (loop_str);
  return return_string;
}

int main ()
{
  char * expanded_string = expand ("1-3a-gM-T3-5"); 
  printf ("%s\n", expanded_string);
  return 0;
}
